#pragma once

#include <cmath>

#include "base.h"
#include "../impl/s2.h"

namespace mdtx::online::updater
{
    template <typename T>
    class covxy : public updater_binary1<T>,
                  public impl::covxy<T>
    {
        using base = updater_binary1<T>;
        using ibase = impl::covxy<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::insert;
        using base::remove;

        using ibase::insert_impl;
        using ibase::remove_impl;
        using ibase::reset_impl;

        void insert(tuple_in const &in) override
        {
            insert_impl(std::get<0>(in), std::get<1>(in));
        }

        void remove(tuple_in const &in) override
        {
            remove_impl(std::get<0>(in), std::get<1>(in));
        }

        void reset() override
        {
            reset_impl();
        }

        tuple_out get() override
        {
            T val{};
            if (this->n > 1)
            {
                val = this->sxy / (this->n - 1);
            }
            return tuple_out(val);
        }
    };

    template <typename T>
    class covxy_ew : public updater_binary1<T>,
                     public impl::covxy_ew<T>
    {
        using base = updater_binary1<T>;
        using ibase = impl::covxy_ew<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::insert;
        using base::remove;

        using ibase::ibase;
        using ibase::insert_impl;
        using ibase::reset_impl;

        explicit covxy_ew(ptrdiff_t period)
            : ibase(T(2) / (period + 1))
        {
        }

        void insert(tuple_in const &in) override
        {
            insert_impl(std::get<0>(in), std::get<1>(in));
        }

        void reset() override
        {
            reset_impl();
        }

        tuple_out get() override
        {
            return tuple_out(this->sxy);
        }
    };

    template <typename T>
    class corrxy : public updater_binary1<T>,
                   public impl::corrxy<T>
    {
        using base = updater_binary1<T>;
        using ibase = impl::corrxy<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::insert;
        using base::remove;

        using ibase::insert_impl;
        using ibase::remove_impl;
        using ibase::reset_impl;

        void insert(tuple_in const &in) override
        {
            insert_impl(std::get<0>(in), std::get<1>(in));
        }

        void remove(tuple_in const &in) override
        {
            remove_impl(std::get<0>(in), std::get<1>(in));
        }

        void reset() override
        {
            reset_impl();
        }

        tuple_out get() override
        {
            T val{};
            if (this->sxsy > 0)
            {
                val = this->sxy / std::sqrt(this->sxsy);
            }
            return tuple_out(val);
        }
    };

    template <typename T>
    class corrxy_ew : public updater_binary1<T>,
                      public impl::corrxy_ew<T>
    {
        using base = updater_binary1<T>;
        using ibase = impl::corrxy_ew<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::insert;
        using base::remove;

        using ibase::ibase;
        using ibase::insert_impl;
        using ibase::reset_impl;

        explicit corrxy_ew(ptrdiff_t period)
            : ibase(T(2) / (period + 1))
        {
        }

        void insert(tuple_in const &in) override
        {
            insert_impl(std::get<0>(in), std::get<1>(in));
        }

        void reset() override
        {
            reset_impl();
        }

        tuple_out get() override
        {
            T val{};
            if (this->sxsy > 0)
            {
                val = this->sxy / std::sqrt(this->sxsy);
            }
            return tuple_out(val);
        }
    };

    template <typename T>
    class beta : public updater_binary1<T>,
                 public impl::beta<T>
    {
        using base = updater_binary1<T>;
        using ibase = impl::beta<T>;

    public:
        using typename base::tuple_in;
        using typename base::tuple_out;
        using base::insert;
        using base::remove;

        using ibase::insert_impl;
        using ibase::remove_impl;
        using ibase::reset_impl;

        void insert(tuple_in const &in) override
        {
            insert_impl(std::get<0>(in), std::get<1>(in));
        }

        void remove(tuple_in const &in) override
        {
            remove_impl(std::get<0>(in), std::get<1>(in));
        }

        void reset() override
        {
            reset_impl();
        }

        tuple_out get() override
        {
            T val{};
            if (this->sx > 0)
            {
                val = this->sxy / this->sx;
            }
            return tuple_out(val);
        }
    };
}
